#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>

int main(int argc, char *argv[]) {
	int fd_src, fd_dst;
	char *buf;
	int fd_src_length;
	struct stat src_st,des_st;
	int n, m;

	if (argc < 3) {
		printf("mycp: Arguments error.\nusage: execFile sourceFilePath destinationPath\n");
		exit(0);
	}
	
	/**
	* 获取源文件路径 argv[1]，获取目标文件路径 argv[2]
	* 打开源文件
	* 扩展：源文件不存在时，给出错误提示信息
	* open 用于打开或创建文件，成功返回文件描述符，失败返回-1
	* O_RONLY 以只读方式打开
	**/ 
	fd_src = open(argv[1], O_RDONLY);
	if (fd_src == -1) {
        printf("errMsg: source File does not exist.\n");
		perror(argv[1]);
        exit(1);
	}
	
	/**
	* 扩展：检查源文件路径是否是目录
	* stat 读取文件属性
	* st_mode 文件类型与访问权限
	**/
	stat(argv[1], &src_st);
	if (S_ISDIR(src_st.st_mode)) {
		printf("errMsg: source File cannot be a directory\n");
		exit(1);
	}

	/**
	* 获取源文件路径 argv[1]，获取目标文件路径 argv[2]
	* 打开目标文件（不存在则创建）
	* O_CREAT 不存在则创建
	* O_TRUNC 若文件以可写方式打开，则令文件长度清0，原来存于该文件的资料消失
	* 0666是八进制的文件许可位，第一位0表示八进制，后三位分别表示所有者，所属组，其他用户的权限 
	**/ 
	char *pSrc, *pDes;  //分别指向源文件路径和目标文件路径
	stat(argv[2], &des_st);
	pDes = argv[2];

	if (S_ISDIR(des_st.st_mode)) {
		// 目标路径是目录，在该目录下创建一个同名文件

		/* 找出源文件的文件名 */
		int len = (int)strlen(argv[1]);
		pSrc = argv[1] + (len-1);
		while (pSrc >= argv[1] && *pSrc != '/') {
			pSrc--;
		}
		pSrc++;

		/* 在目标路径后添加文件名 */
		len = (int)strlen(argv[2]);
		if ((len == 1 && *(argv[2]) == '.') || (len == 2 && *(argv[2]) == '.' && *(argv[2]+1) == '/')) {
			// 复制到当前目录
			pDes = argv[1];
		} else {
			// 复制到某目录下
			pDes = argv[2];
			if (*(pDes + (len-1)) != '/') {
				//目录路径最后缺少'/'，需要补上
				strcat(pDes, "/");
			}
			strcat(pDes+len, pSrc);
		}
	}

    /**
	* 先读取源文件内容，避免在当前目录覆盖源文件时，把内容清空
    * 源文件读取一定数据写到缓冲区 buf
    * lseek 用于设置或查询文件当前位置
    * SEEK_END 文件当前位置 = 文件尾 + offset（字节），可以读出文件长度，但一定要把当前的文件位置设置回开头，否则下次读不到东西
	**/
    fd_src_length = lseek(fd_src, 0, SEEK_END);
    if (fd_src_length == -1) {
        exit(1); 
    }
    
    buf = (char *)malloc((long) (fd_src_length+1));
    if (buf == NULL) {
        printf("mycp: Malloc error\n");
    }
    
    lseek(fd_src, 0, SEEK_SET); // 前面读取了文件长度，要把当前的文件位置设置回开头，否则读不到东西
    n = read(fd_src, buf, fd_src_length);

    /**
	* 扩展：目标文件存在时，提示“是否覆盖”or“是否合并”等提示信息
	* access 测试文件权限，F_OK测试文件是否存在，存在返回0，不存在返回-1
	**/
    int flag = 1;  // 1:覆盖或文件不存在情况 0:文件存在且合并
    if (access(pDes, 0) == 0) {
        printf("Target file exists, if you want to overwrite(Enter 1) or merge(Enter 2)?\n");
        int numEnter;
        scanf("%d", &numEnter);
        while (numEnter != 1 && numEnter != 2) {
            printf("Please enter 1(overwrite) or 2(merge)!\n");
            scanf("%d", &numEnter);
            printf("%d", numEnter);
        }
        if (numEnter == 1) {
            // 覆盖文件
            
        } else {
            // 合并文件
            flag = 0;
        }
    }

    if (flag == 1) {
        fd_dst = open(pDes, O_WRONLY|O_CREAT|O_TRUNC, 0666);
    } else if (flag == 0) {
        fd_dst = open(pDes, O_WRONLY|O_APPEND, 0666);
    }

    if (fd_dst == -1) {
        perror(pDes);
    }
    
    /* 缓冲区写入目标文件 */
    if (n >= 0) {
        /* 把读取部分写入目标文件 */
        m = write(fd_dst, buf, n);
        if (n != m) {
            printf("Copy error\n");
        } else {
            printf("Copy success\n");
        }
    } else {
        printf("mycp: Read error\n");
        exit(1);
    }
	
	/**
	* 关闭源文件
	* 关闭目标文件
	**/
	close(fd_src);
	close(fd_dst);
	free(buf);
	
	return 0;
} 